import java.awt.Color;

import color.LabColorSpace;
import color.RgbHsv;

import ij.IJ;
import ij.ImagePlus;
import ij.gui.GenericDialog;
import ij.gui.Toolbar;
import ij.plugin.filter.PlugInFilter;
import ij.process.ImageProcessor;



public class A06_SkinColorDetection implements PlugInFilter{

	int choice = 0;
	double radius = 50.0;
	double radiusLAB = 15.0;
	double huediff = Math.toRadians(10.0);
	double satdiff = 0.7;
	double valdiff = 0.5;
	ImageProcessor result;
	int[] white = {255, 255, 255};
	int[] green = {0, 255, 0};
	
	public int setup(String arg0, ImagePlus img) {
		return DOES_RGB;	// RGB images
	}

	public void run(ImageProcessor ip) {
		result = ip.duplicate();
		if(SelectColorSpace())
		{
			if(choice == 0)	//RGB
			{
				FindSkinColorInRGB(ip);
			} else if(choice == 1)	//HSV
			{
				FindSkinColorInHSV(ip);
			} else if(choice == 2)	//LAB
			{
				FindSkinColorInLAB(ip);
			}
		}else
		{
			IJ.log("Gib mal was gscheites ein!");
		}
	}

	private void FindSkinColorInHSV(ImageProcessor ip) {
		IJ.log("HSV");
		//Convert image to HSV
		float[][][] HSV = new float[ip.getWidth()][ip.getHeight()][3];
		int[] rgb = new int[3];
		float[] hsv = new float[3];
		
		for(int i = 0; i < ip.getWidth(); i++)
		{
			for(int j = 0; j < ip.getHeight(); j++)
			{
				ip.getPixel(i, j, rgb);
				hsv = RgbHsv.RGBtoHSV(rgb, hsv);
				//IJ.log(""+hsv[0]+" | "+hsv[1]+" | "+hsv[2]);
				HSV[i][j][0] = hsv[0];
				HSV[i][j][1] = hsv[1];
				HSV[i][j][2] = hsv[2];
				//IJ.log(""+HSV[i][j][0]+" | "+HSV[i][j][1]+" | "+HSV[i][j][2]);
			}
		}
		//Referenz farbe holen
		Color ref = GetReferencePixel();
		rgb = new int[3];
		rgb[0] = ref.getRed();
		rgb[1] = ref.getGreen();
		rgb[2] = ref.getBlue();
		float[] refCol = new float[3];
		refCol = RgbHsv.RGBtoHSV(rgb, refCol);
		
		for(int i = 0; i < ip.getWidth(); i++)
		{
			for(int j = 0; j < ip.getHeight(); j++)
			{
				float[] sample = new float[3];
				sample[0] = HSV[i][j][0];
				sample[1] = HSV[i][j][1];
				sample[2] = HSV[i][j][2];
				//IJ.log(""+sample[0]);
				if(IsSkinColorHSV(sample, refCol))
				{
					result.putPixel(i, j, white);
				} else
				{
					rgb = ip.getPixel(i, j, rgb);
					result.putPixel(i, j, rgb);
				}
			}
		}
		(new ImagePlus("Find skin Color HSV", result)).show();
	}

	private void FindSkinColorInLAB(ImageProcessor ip) {
		IJ.log("LAB");
		//In LAB convertieren
		float[][][] LAB = new float[ip.getWidth()][ip.getHeight()][3];
		float[] rgb = new float[3];
		int[] RGB = new int[3];
		float[] lab = new float[3];
		LabColorSpace lcs = new LabColorSpace(); 
		
		for (int v = 0; v < ip.getHeight(); v++) {
			for (int u = 0; u < ip.getWidth(); u++) {
				ip.getPixel(u, v, RGB);
				
				rgb[0] = RGB[0] / 255.0f;
				rgb[1] = RGB[1] / 255.0f;
				rgb[2] = RGB[2] / 255.0f;
				
				lab = lcs.fromRGB(rgb);	// convert sRGB -> Lab
				LAB[u][v][0] = lab[0];
				LAB[u][v][1] = lab[1];
				LAB[u][v][2] = lab[2];
			}
		}
		//Referenz farbe holen
		Color ref = GetReferencePixel();
		RGB = new int[3];
		RGB[0] = ref.getRed();
		RGB[1] = ref.getGreen();
		RGB[2] = ref.getBlue();
		float[] refCol = new float[3];
		rgb[0] = RGB[0] / 255.0f;
		rgb[1] = RGB[1] / 255.0f;
		rgb[2] = RGB[2] / 255.0f;
		refCol = lcs.fromRGB(rgb);
		
		for(int i = 0; i < ip.getWidth(); i++)
		{
			for(int j = 0; j < ip.getHeight(); j++)
			{
				float[] sample = new float[3];
				sample[0] = LAB[i][j][0];
				sample[1] = LAB[i][j][1];
				sample[2] = LAB[i][j][2];
				//IJ.log(""+sample[0]);
				if(IsSkinColorLAB(sample, refCol))
				{
					result.putPixel(i, j, white);
				} else
				{
					RGB = ip.getPixel(i, j, RGB);
					result.putPixel(i, j, RGB);
				}
			}
		}
		(new ImagePlus("Find skin Color HSV", result)).show();
	}

	private void FindSkinColorInRGB(ImageProcessor ip) {
		IJ.log("RGB");
		
		Color ref = GetReferencePixel();
		int[] refCol = new int[3];
		refCol[0] = ref.getRed();
		refCol[1] = ref.getGreen();
		refCol[2] = ref.getBlue();
		
		for(int i = 0; i < ip.getWidth(); i++)
		{
			for(int j = 0; j < ip.getHeight(); j++)
			{
				int[] sample = new int[3];
				ip.getPixel(i, j, sample);
				
				if(IsSkinColorRGB(sample, refCol))
				{
					result.putPixel(i, j, white);
				} else
				{
					//result.putPixel(i, j, white);
				}
			}
		}
		
		(new ImagePlus("Find skin Color RGB", result)).show();
	}

	private boolean IsSkinColorRGB(int[] sample, int[] refCol) {
		double diffR = sample[0] - refCol[0];
		double diffG = sample[1] - refCol[1];
		double diffB = sample[2] - refCol[2];
		double dist = Math.sqrt(Math.pow(diffR, 2)+Math.pow(diffG, 2)+Math.pow(diffB, 2));
		//IJ.log(""+dist);
		if(dist <= radius)
		{
			return true;
		}else
		{
			return false;
		}
	}
	
	private boolean IsSkinColorHSV(float[] sample, float[] refCol) {
		//Hue 
		//IJ.log(""+sample[0]+" | "+(refCol[0] - huediff/2.0)+" | "+(refCol[0] + huediff/2.0));
		if(sample[0] > refCol[0] - huediff/2.0 && sample[0] < refCol[0] + huediff/2.0)
		{
			//Saturation
			if(sample[1] > refCol[1] - satdiff/2.0 && sample[1] < refCol[1] + satdiff/2.0)
			{
				//Brightness
				if(sample[2] > refCol[2] - valdiff/2.0 && sample[2] < refCol[2] + valdiff/2.0)
				{
					return true;
				}
			}
		}
		return false;
	}

	private boolean IsSkinColorLAB(float[] sample, float[] refCol) {
		double diffL = sample[0] - refCol[0];
		double diffA = sample[1] - refCol[1];
		double diffB = sample[2] - refCol[2];
		double dist = Math.sqrt(Math.pow(diffL, 2)+Math.pow(diffA, 2)+Math.pow(diffB, 2));
		//IJ.log(""+dist);
		if(dist <= radiusLAB)
		{
			return true;
		}else
		{
			return false;
		}
	}
	
	private Color GetReferencePixel() {
		Toolbar.setForegroundColor(null);
		return Toolbar.getForegroundColor();
	}

	private boolean SelectColorSpace() {
		GenericDialog gd = new GenericDialog("Select Desired Color Space");
		String[] titels = {"RGB", "HSV", "LAB"};
		gd.addChoice("Color space:", titels, "RGB");
		gd.showDialog();
		
		if(gd.wasCanceled())
		{
			return false;
		}else
		{
			int index = gd.getNextChoiceIndex();
			choice = index;
			return true;
		}
	}

	
}
